﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Xml.Serialization;

using Katamino.Model.Data;

using SolverTemplateLib.BackTracker;

namespace Katamino
{
    // [Serializable] -> ISerializable
    [XmlRoot("Solution")]
    [Serializable]
    public class KataminoBacktracker : Backtracker, ISerializable
    {
        // static data, needed once
        private static KataminoForm form;

        // Database stuff and configuration
        private static Katamino.Model.Data.KataminoDataSetTableAdapters.SolutionTableAdapter ta;
        private static Katamino.Model.Data.KataminoDataSet.SolutionDataTable solutionDataTable;

        // Make them Lazy initialized singletons
        private static Katamino.Model.Data.KataminoDataSetTableAdapters.SolutionTableAdapter GetSolutionTableAdapter()
        {
            if (ta == null)
                ta = new Katamino.Model.Data.KataminoDataSetTableAdapters.SolutionTableAdapter();

            return ta;
        }

        private static Katamino.Model.Data.KataminoDataSet.SolutionDataTable GetSolutionDataTable()
        {
            if (solutionDataTable == null)
                solutionDataTable = GetSolutionTableAdapter().GetData();
            return solutionDataTable;
        }

        // local data, for each partial solution
        private List<byte> _pentaSet;

        [XmlArray]
        public List<byte> _solSet;
        private List<byte> _rotSet;
        public List<byte> _locSet;

        private BoardWithGraphics _b; // Board contains its bitmap

        // parameterless constructor for XML serialization!?!
        public KataminoBacktracker() { }

        public KataminoBacktracker(int w, int h)
        {
            Board = new Board(w, h);
            problem = problem ?? new KataminoProblem(w, h);
        }

        public Board Board;

        public List<Penta> Variations { get { return problem.Variations; } }

        public static KataminoProblem problem;
        private KataminoBacktracker parent;
        public KataminoBacktracker Parent { get { return parent; } }

        // create initial / empty / partial solution
        public KataminoBacktracker(KataminoForm k, int w, int h)
        {
            Board = new Board(w, h);

            // set static data
            form = k;

            // set and initialize data
            _pentaSet = new List<byte>();
            _solSet = new List<byte>();
            _rotSet = new List<byte>();
            _locSet = new List<byte>();
            for (byte i = 0; i < 12; i++) _pentaSet.Add(i); // some Range method!?!
            _b = new BoardWithGraphics(w, h);
            _b.InitializeGraphics(form.ImageSize().Width, form.ImageSize().Height);
        }

        private KataminoBacktracker(BoardWithGraphics b, List<byte> pentaSet, List<byte> solutionSet, List<byte> rotationSet, List<byte> locationSet)
        {
            _b = b;
            _pentaSet = pentaSet;
            _solSet = solutionSet;
            _rotSet = rotationSet;
            _locSet = locationSet;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns>
        /// </returns>
        public override bool IsCancelled()
        {
            if (form != null)
                return form.KataminoBackgroundWorker().CancellationPending;
            
            return false;
        }

        public override bool IsSolution()
        {
            return (Board.UnusedPentas == 0);
        }

        public override void ProcessCandidate()
        {
            if (form == null) return;

            // TODO: Make this more precise, including rotations?!
            // TODO: move this to the highest level foreach, so this occurs only occasionally
            if (_solSet.Count > 9)
                form.KataminoBackgroundWorker().ReportProgress(_solSet[0]*8 + _solSet[1]/2);
//              form.KataminoBackgroundWorker().ReportProgress((_b.D[0, 0] - 1) * 9, _b.picture);
        }
        public override void ProcessSolution()
        {
            Debug.Assert(form.InvokeRequired,
                "Threading Error",
                "For Katamino, this thread should not run on UI thread");

            // tell UI thread its progress and to draw and store solution
            form.UpdatePicture(_b);

            // 
            var newRow = GetSolutionDataTable().NewSolutionRow();
            newRow.dateTimeFound = DateTime.Now;

            newRow.p1 = _solSet[0];
            newRow.p2 = _solSet[1];
            newRow.p3 = _solSet[2];
            newRow.p4 = _solSet[3];
            newRow.p5 = _solSet[4];
            newRow.p6 = _solSet[5];
            newRow.p7 = _solSet[6];
            newRow.p8 = _solSet[7];
            newRow.p9 = _solSet[8];
            newRow.p10= _solSet[9];
            newRow.p11= _solSet[10];
            newRow.p12= _solSet[11];

            newRow.r1 = _rotSet[0];
            newRow.r2 = _rotSet[1];
            newRow.r3 = _rotSet[2];
            newRow.r4 = _rotSet[3];
            newRow.r5 = _rotSet[4];
            newRow.r6 = _rotSet[5];
            newRow.r7 = _rotSet[6];
            newRow.r8 = _rotSet[7];
            newRow.r9 = _rotSet[8];
            newRow.r10 = _rotSet[9];
            newRow.r11 = _rotSet[10];
            newRow.r12 = _rotSet[11];

            newRow.l1 = _locSet[0];
            newRow.l2 = _locSet[1];
            newRow.l3 = _locSet[2];
            newRow.l4 = _locSet[3];
            newRow.l5 = _locSet[4];
            newRow.l6 = _locSet[5];
            newRow.l7 = _locSet[6];
            newRow.l8 = _locSet[7];
            newRow.l9 = _locSet[8];
            newRow.l10 = _locSet[9];
            newRow.l11 = _locSet[10];
            newRow.l12 = _locSet[11];

            lock (solutionDataTable)
            {
                GetSolutionDataTable().AddSolutionRow(newRow);

                GetSolutionTableAdapter().Update(GetSolutionDataTable());

                form.ReportSolution(GetSolutionDataTable().Rows.Count);
            }

        }

        /// <summary>
        /// Overruled method of Backtracker template function to determine candidates for backtrack continuation
        /// </summary>
        /// <returns></returns>
        public override List<Backtracker> Candidates()
        {
            List<Backtracker> result = new List<Backtracker>();

            int firstFree = Board.firstFree();

            // find first free field
            if (firstFree < Board.Width * Board.Height)
            {
                // iterate through unused pentas
                foreach (int pentaid in problem.BitFinder.EnumerateBits(Board.UnusedPentas))
                {
                    foreach (Penta _pt in problem.Variations.Where(penta => penta.Id == pentaid))
                    {
                        // return null if placement can not be made.
                        var newBoard = Board.AddPenta(_pt);

                        if (newBoard != null)
                        {
                            result.Add(new KataminoBacktracker(this, newBoard));
                        }
                    }
                }
            }

            return result;
        }

        public void Finish()
        {
            form.KataminoBackgroundWorker().ReportProgress(100);
        }

        // The ISerializable interface: for serialization fill info
        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter=true)]
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("SolSet", this._solSet, _solSet.GetType());
            info.AddValue("RotSet", this._rotSet, _rotSet.GetType());
            info.AddValue("LocSet", this._locSet, _locSet.GetType());
        }
        // For deserialization: define constructor that takes SerializationInfo and StreamingContext
        protected KataminoBacktracker(SerializationInfo info, StreamingContext context)
        {
            _solSet = (List<byte>)info.GetValue("Solution", _solSet.GetType());
            _rotSet = (List<byte>)info.GetValue("Solution", _rotSet.GetType());
            _locSet = (List<byte>)info.GetValue("Solution", _locSet.GetType());
        }

        public KataminoBacktracker(KataminoBacktracker kataminoBacktracker, Katamino.Board newBoard)
        {
            // TODO: Complete member initialization
            this.parent = kataminoBacktracker;
            this.Board = newBoard;
        }
    }
}
